#include "../pch.h"
#include "REFXMLEntityReference.h"

namespace BFX
{

REFXMLNode::REFXMLNode()
{
	m_pParentNode = NULL;
}

REFXMLNode::REFXMLNode(REFXMLDocument* pDoc)
{
	BFX_REQUIRE(pDoc != NULL && "Cannot create a node without an owner document.");

	m_pParentNode = pDoc;
}

REFXMLNode::~REFXMLNode()
{
}

// Gets or sets the value of the node.
UniString REFXMLNode::GetValue() const
{
	return UniString();
}

void REFXMLNode::SetValue(const UniString& strValue)
{
	BFX_ENSURE1(false, "Cannot set a value on wrong node type.");
}

// Gets the namespace URI of this node.
UniString REFXMLNode::GetNamespaceURI() const
{
	return UniString();
}

// Gets or sets the namespace prefix of this node.
UniString REFXMLNode::GetPrefix() const
{
	return UniString();
}

void REFXMLNode::SetPrefix(const UniString& str)
{
}

// Gets the parent of this node (for nodes that can have parents).
REFXMLNode* REFXMLNode::GetParentNode() const
{
	BFX_ASSERT(m_pParentNode != NULL);

	if (m_pParentNode->GetNodeType() != XMLNodeType_Document)
		return m_pParentNode;

	// Linear lookup through the children of the document 
	REFXMLLinkedNode* pFirstChild = (REFXMLLinkedNode*)m_pParentNode->GetFirstChild();
	if (pFirstChild != NULL)
	{
		REFXMLLinkedNode* pNode = pFirstChild;
		do
		{
			if (pNode == this)
				return m_pParentNode;
			pNode = pNode->m_pNext;
		}
		while (pNode != NULL);
	}

	return NULL;
}

	// Gets all children of this node.
REFXMLNodeList* REFXMLNode::GetChildNodes() const
{
	REFXMLNodeList* pList = new REFXMLChildList(this);
	pList->Autorelease();
	return pList;
}

// Gets the node immediately preceding this node.
REFXMLNode* REFXMLNode::GetPreviousSibling() const
{
	return NULL;
}

// Gets the node immediately following this node.
REFXMLNode* REFXMLNode::GetNextSibling() const
{
	return NULL;
}

// Gets a collection containing the attributes of this node.
REFXMLAttributeMap* REFXMLNode::GetAttributes() const
{
	return NULL;
}

// Gets the XmlDocument that contains this node.
REFXMLDocument* REFXMLNode::GetOwnerDocument() const
{
	BFX_ASSERT (m_pParentNode != NULL);

	if (m_pParentNode->GetNodeType() == XMLNodeType_Document)
		return (REFXMLDocument*)m_pParentNode;
	return m_pParentNode->GetOwnerDocument();
}

// Gets the first child of this node.
REFXMLNode* REFXMLNode::GetFirstChild() const
{
	return GetFirstNode();
}

// Gets the last child of this node.
REFXMLNode* REFXMLNode::GetLastChild() const
{
	return GetLastNode();
}

// Inserts the specified node immediately before the specified reference node.
REFXMLNode* REFXMLNode::InsertBefore(REFXMLNode* pNewChild, const REFXMLNode* pRefChild)
{
	BFX_REQUIRE (this != pNewChild && !AncestorNode(pNewChild) && "Cannot insert a node or any ancestor of that node as a child of itself.");
	BFX_REQUIRE (IsContainer() && "The current node cannot contain other nodes.");
	BFX_REQUIRE (pRefChild->GetParentNode() == this && "The reference node is not a child of this node.");

	if (pRefChild == NULL)
		AppendChild(pNewChild);

	if (pNewChild == pRefChild)
		return pNewChild;

	REFXMLDocument* pChildDoc = pNewChild->GetOwnerDocument();
	REFXMLDocument* pThisDoc = GetOwnerDocument();

	BFX_REQUIRE ((pChildDoc == NULL || pChildDoc == pThisDoc || pChildDoc == this) && "The node to be inserted is from a different document context.");

	if (pNewChild->GetParentNode() != NULL)
		pNewChild->GetParentNode()->RemoveChild(pNewChild);

	// special case for doc-fragment.
	if (pNewChild->GetNodeType() == XMLNodeType_DocumentFragment)
	{
		REFXMLNode* pFirst = pNewChild->GetFirstChild();
		REFXMLNode* pNode = pFirst;
		if (pNode != NULL)
		{
			pNewChild->RemoveChild(pNode);
			InsertBefore(pNode, pRefChild);
			// insert the rest of the children after this one.
			InsertAfter(pNewChild, pNode);
		}
		pThisDoc->NotifyChanged();
		return pFirst;
	}

	BFX_REQUIRE (IsValidChildType(pNewChild->GetNodeType()) && "The specified node cannot be inserted as the valid child of this node, because the specified node is the wrong type.");

	REFXMLLinkedNode* pNewNode = (REFXMLLinkedNode*) pNewChild;
	REFXMLLinkedNode* pRefNode = (REFXMLLinkedNode*) pRefChild;

	pNewNode->m_pPrev = pRefNode->m_pPrev;
	pNewNode->m_pNext = pRefNode;
	pRefNode->m_pPrev = pNewNode;
	pNewNode->SetParent(this);

	// special case for text node.
	if (pRefNode == GetFirstChild())
	{
		SetFirstNode(pNewNode);

		if (pNewNode->IsText() && pRefNode->IsText())
		{
			NestTextNodes(pNewNode, pRefNode);
		}
	}
	else
	{
		REFXMLLinkedNode* pPrevNode = (REFXMLLinkedNode*) pRefNode->GetPreviousSibling();
		if (pPrevNode->IsText())
		{
			if (pNewNode->IsText())
			{
				NestTextNodes(pPrevNode, pNewNode);
				if (pRefNode->IsText())
				{
					NestTextNodes(pNewNode, pRefNode);
				}
			}
			else if (pRefNode->IsText())
			{
				UnnestTextNodes(pPrevNode, pRefNode);
			}
		}
		else
		{
			if (pNewNode->IsText() && pRefNode->IsText())
			{
				NestTextNodes(pNewNode, pRefNode);
			}
		}
	}

	pThisDoc->NotifyChanged();
	return pNewChild;
}

// Inserts the specified node immediately after the specified reference node.
REFXMLNode* REFXMLNode::InsertAfter(REFXMLNode* pNewChild, const REFXMLNode* pRefChild)
{
	BFX_REQUIRE (this != pNewChild && !AncestorNode(pNewChild) && "Cannot insert a node or any ancestor of that node as a child of itself.");
	BFX_REQUIRE (IsContainer() && "The current node cannot contain other nodes.");
	BFX_REQUIRE (pRefChild->GetParentNode() == this && "The reference node is not a child of this node.");

	if (pRefChild == NULL)
		return PrependChild(pNewChild);

	if (pNewChild == pRefChild)
		return pNewChild;

	REFXMLDocument* pChildDoc = pNewChild->GetOwnerDocument();
	REFXMLDocument* pThisDoc = GetOwnerDocument();

	BFX_REQUIRE ((pChildDoc == NULL || pChildDoc == pThisDoc || pChildDoc == this) && "The node to be inserted is from a different document context.");

	// TODO: CanInsertAfter ???

	if (pNewChild->GetParentNode() != NULL)
		pNewChild->GetParentNode()->RemoveChild(pNewChild);

	// special case for doc-fragment.
	if (pNewChild->GetNodeType() == XMLNodeType_DocumentFragment)
	{
		const REFXMLNode* pLast = pRefChild;
		REFXMLNode* pFirst = pNewChild->GetFirstChild();
		REFXMLNode* pNode = pFirst;
		while (pNode != NULL)
		{
			REFXMLNode* pNext = pNode->GetNextSibling();
			pNewChild->RemoveChild(pNode);
			InsertAfter(pNode, pLast);
			pLast = pNode;
			pNode = pNext;
		}
		return pFirst;
	}

	BFX_REQUIRE (IsValidChildType(pNewChild->GetNodeType()) && "The specified node cannot be inserted as the valid child of this node, because the specified node is the wrong type.");

	REFXMLLinkedNode* pNewNode = (REFXMLLinkedNode*) pNewChild;
	REFXMLLinkedNode* pRefNode = (REFXMLLinkedNode*) pRefChild;

	pNewNode->m_pPrev = pRefNode;
	pNewNode->m_pNext = pRefNode->m_pNext;
	pRefNode->m_pNext = pNewNode;
	pNewNode->SetParent(this);

	// special case for text node.
	if (pRefNode == GetLastNode())
	{
		SetLastNode(pNewNode);

		if (pRefNode->IsText())
		{
			if (pNewNode->IsText())
			{
				NestTextNodes(pRefNode, pNewNode);
			}
		}
	}
	else
	{
		REFXMLLinkedNode* pNextNode = pRefNode->m_pNext;
		pNewNode->m_pNext = pNextNode;
		pRefNode->m_pNext = pNewNode;
		pNewNode->SetParent(this);

		if (pRefNode->IsText())
		{
			if (pNewNode->IsText())
			{
				NestTextNodes(pRefNode, pNewNode);
				if (pNextNode->IsText())
				{
					NestTextNodes(pNewNode, pNextNode);
				}
			}
			else if (pNextNode->IsText())
			{
				UnnestTextNodes(pRefNode, pNextNode);
			}
		}
		else
		{
			if (pNewNode->IsText() && pNewNode->IsText())
			{
				NestTextNodes(pNewNode, pNextNode);
			}
		}
	}

	pThisDoc->NotifyChanged();
	return pNewNode;
}

// Replaces the child node oldChild with newChild node.
REFXMLNode* REFXMLNode::ReplaceChild(REFXMLNode* pNewChild, REFXMLNode* pOldChild)
{
	REFXMLNode* pNextNode = pOldChild->GetNextSibling();
	RemoveChild(pOldChild);
	/*REFXMLNode* pNode = */InsertBefore(pNewChild, pNextNode);
	return pOldChild;
}

// Removes specified child node.
REFXMLNode* REFXMLNode::RemoveChild(REFXMLNode* pOldChild)
{
	BFX_REQUIRE (IsContainer() && "The current node cannot contain other nodes, so the node to be removed is not its child.");
	BFX_REQUIRE (pOldChild->GetParentNode() == this && "The node to be removed is not a child of this node.");

    REFXMLLinkedNode* pOldNode = (REFXMLLinkedNode*) pOldChild;
	pOldNode->Autorelease();

	REFXMLDocument* pThisDoc = GetOwnerDocument();

	REFXMLLinkedNode* pPrevNode = pOldNode->m_pPrev;
	REFXMLLinkedNode* pNextNode = pOldNode->m_pNext;

	if (pOldNode == GetFirstChild())
	{
		SetFirstNode(pNextNode);

		if (pOldNode == GetLastChild())
		{
			SetLastNode(NULL);
		}
		else
		{
			if (pNextNode->IsText() && pOldNode->IsText())
			{
				UnnestTextNodes(pOldNode, pNextNode);
			}
		}
	}
	else
	{
		if (pOldNode == GetLastChild())
		{
			SetLastNode(pPrevNode);
		}
		else
		{
			if (pNextNode->IsText())
			{
				if (pPrevNode->IsText())
				{
					NestTextNodes(pPrevNode, pNextNode);
				}
				else
				{
					if (pOldNode->IsText())
					{
						UnnestTextNodes(pOldNode, pNextNode);
					}
				}
			}
		}
	}
	if (pPrevNode != NULL)
		pPrevNode->m_pNext = pNextNode;
	if (pNextNode != NULL)
		pNextNode->m_pPrev = pPrevNode;

	pOldNode->m_pPrev = NULL;
	pOldNode->m_pNext = NULL;
	pOldNode->SetParent(NULL);

	pThisDoc->NotifyChanged();

	return pOldChild;
}

// Adds the specified node to the beginning of the list of children of this node.
REFXMLNode* REFXMLNode::PrependChild(REFXMLNode* pNewChild)
{
	return InsertBefore(pNewChild, GetFirstChild());
}

// Adds the specified node to the end of the list of children of this node.
REFXMLNode* REFXMLNode::AppendChild(REFXMLNode* pNewChild)
{
	REFXMLDocument* pThisDoc = GetOwnerDocument();
	if (pThisDoc == NULL)
	{
		BFX_ASSERT(GetNodeType() == XMLNodeType_Document);
		pThisDoc = (REFXMLDocument*)this;
	}

	BFX_REQUIRE (this != pNewChild && !AncestorNode(pNewChild) && "Cannot insert a node or any ancestor of that node as a child of itself.");
	BFX_REQUIRE (IsContainer() && "The current node cannot contain other nodes.");

	if (pNewChild->GetParentNode() != NULL)
		pNewChild->GetParentNode()->RemoveChild(pNewChild);

	REFXMLDocument* pChildDoc = pNewChild->GetOwnerDocument();

	BFX_REQUIRE ((pChildDoc == NULL || pChildDoc == pThisDoc || pChildDoc == this) && "The node to be inserted is from a different document context.");

	// special case for doc-fragment.
	if (pNewChild->GetNodeType() == XMLNodeType_DocumentFragment)
	{
		REFXMLNode* pFirst = pNewChild->GetFirstChild();
		REFXMLNode* pNode = pFirst;
		while (pNode != NULL)
		{
			REFXMLNode* pNext = pNode->GetNextSibling();
			pNewChild->RemoveChild(pNode);
			AppendChild(pNode);
			pNode = pNext;
		}
		pThisDoc->NotifyChanged();

		return pFirst;
	}

	BFX_REQUIRE (IsValidChildType(pNewChild->GetNodeType()) && "The specified node cannot be inserted as the valid child of this node, because the specified node is the wrong type.");

	REFXMLLinkedNode* pNewNode = (REFXMLLinkedNode*) pNewChild;
	REFXMLLinkedNode* pRefNode = GetLastNode();

	pNewNode->m_pPrev = pRefNode;

	if (pRefNode != NULL)
	{
		pRefNode->m_pNext = pNewNode;

		if (pRefNode->IsText() && pNewNode->IsText())
		{
			NestTextNodes(pRefNode, pNewNode);
		}
	}

	SetLastNode(pNewNode);
	if (GetFirstNode() == NULL)
		SetFirstNode(pNewNode);

	pNewNode->SetParent(this);

	pThisDoc->NotifyChanged();

	return pNewNode;
}

// Gets or sets the concatenated values of the node and all its children.
UniString REFXMLNode::GetInnerText() const
{
	REFXMLNode* n = GetFirstChild();
	if (n == NULL)
		return UniString();
	if (n->GetNextSibling() == NULL)
	{
		XMLNodeType nt = n->GetNodeType();
		switch (nt)
		{
		case XMLNodeType_Text:
		case XMLNodeType_CDATA:
		case XMLNodeType_Whitespace:
		case XMLNodeType_SignificantWhitespace:
			return n->GetValue();
		default:
			break;
		}
	}
	UniString strResult;
	AppendChildText(strResult);
	return strResult;
}

void REFXMLNode::SetInnerText(const UniString& str)
{
	REFXMLNode* n = GetFirstChild();
	if (n != NULL						//there is one child
		&& n->GetNextSibling() == NULL	// and exactly one 
		&& n->GetNodeType() == XMLNodeType_Text)	//which is a text node
	{
		//this branch is for perf reason and event fired when TextNode.Value is changed
		n->SetValue(str);
	} 
	else {
		RemoveAll();
		AppendChild(GetOwnerDocument()->CreateTextNode(str));
	}
}

// Gets or sets the markup representing just the children of this node.
UniString REFXMLNode::GetInnerXml() const
{
	REF<REFUniStringWriter> pTextWriter = new REFUniStringWriter();
	REF<REFXMLTextWriter> pWriter = new REFXMLTextWriter(pTextWriter);
	WriteContentTo(pWriter);
	pWriter->Flush();
	return pTextWriter->ToString();
}
void REFXMLNode::SetInnerXml(const UniString& str)
{
	BFX_ENSURE (0 && "Cannot set the 'InnerXml' for the current node because it is either read-only or cannot have children.");
}

// Gets the markup representing this node and all its children.
UniString REFXMLNode::GetOuterXml() const
{
	REF<REFUniStringWriter> pTextWriter = new REFUniStringWriter();
	REF<REFXMLTextWriter> pWriter = new REFXMLTextWriter(pTextWriter);
	WriteTo(pWriter);
	pWriter->Flush();
	return pTextWriter->ToString();
}

UniString REFXMLNode::GetBaseURI() const
{
	REFXMLNode* pCurNode = GetParentNode();	//save one while loop since if going to here, the nodetype of this node can't be document, entity and entityref
	while (pCurNode != NULL)
	{
		XMLNodeType nt = pCurNode->GetNodeType();
		//EntityReference's children come from the dtd where they are defined.
		//we need to investigate the same thing for entity's children if they are defined in an external dtd file.
		if (nt == XMLNodeType_EntityReference)
			return ((REFXMLEntityReference*)pCurNode)->GetChildBaseURI();
		if (nt == XMLNodeType_Document  || nt == XMLNodeType_Entity  || nt == XMLNodeType_Attribute)
			return pCurNode->GetBaseURI();
		pCurNode = pCurNode->GetParentNode();
	}
	return UniString();	// returns empty string if not found.
}

// Removes all the children and/or attributes of the current node.
void REFXMLNode::RemoveAll()
{
	REFXMLNode* pChild = GetFirstChild();
	REFXMLNode* pSibling = NULL;

	while (pChild != NULL)
	{
		pSibling = pChild->GetNextSibling();
		RemoveChild(pChild);
		pChild = pSibling;
	}
}

REFXMLLinkedNode* REFXMLNode::GetFirstNode() const
{
	return NULL;
}

void REFXMLNode::SetFirstNode(REFXMLLinkedNode* pNode)
{
}

REFXMLLinkedNode* REFXMLNode::GetLastNode() const
{
	return NULL;
}

void REFXMLNode::SetLastNode(REFXMLLinkedNode* pNode)
{
}

REFXMLNode* REFXMLNode::FindChild(XMLNodeType type) const
{
	for (REFXMLNode* pChild = GetFirstChild(); pChild != NULL; pChild = pChild->GetNextSibling())
	{
		if (pChild->GetNodeType() == type)
			return pChild;
	}
	return NULL;
}

void REFXMLNode::SetParent(REFXMLNode* pNode)
{
	if (pNode == NULL)
	{
		m_pParentNode = GetOwnerDocument();
	}
	else
	{
		m_pParentNode = pNode;
	}
}

bool REFXMLNode::IsContainer() const
{
	return false;
}

bool REFXMLNode::IsValidChildType(XMLNodeType type) const
{
    return false;
}

bool REFXMLNode::IsText() const
{
	return false;
}

REFXMLNode* REFXMLNode::GetPreviousText() const
{
	return NULL;
}

bool REFXMLNode::AncestorNode(REFXMLNode* pNode) const
{
	REFXMLNode* p = GetParentNode();

	while (p != NULL && p != this)
	{
		if (p == pNode)
			return true;
		p = p->GetParentNode();
	}

	return false;
}

void REFXMLNode::AppendChildText(INOUT UniString& strBuilder) const
{
	for (REFXMLNode* pChild = GetFirstChild(); pChild != NULL; pChild = pChild->GetNextSibling())
	{
		if (pChild->GetFirstChild() == NULL)
		{
			if (pChild->GetNodeType() == XMLNodeType_Text
				|| pChild->GetNodeType() == XMLNodeType_CDATA
				|| pChild->GetNodeType() == XMLNodeType_Whitespace
				|| pChild->GetNodeType() == XMLNodeType_SignificantWhitespace)
				strBuilder.Append(pChild->GetInnerText());
		}
		else
		{
			pChild->AppendChildText(strBuilder);
		}
	}
}

REFXMLDocument* REFXMLNode::GetDocument() const
{
	if (GetNodeType() == XMLNodeType_Document)
		return (REFXMLDocument*)this;
	return GetOwnerDocument();
}

// trace to the top to find out its parent node.
bool REFXMLNode::IsConnected() const
{
	REFXMLNode* pParent = GetParentNode();
	while (pParent != NULL && !(pParent->GetNodeType() == XMLNodeType_Document))
		pParent = pParent->GetParentNode();           
	return pParent != NULL;
}

void REFXMLNode::SplitName(const UniString& strName, OUT UniString& strPrefix, OUT UniString& strLocalName)
{
	int index = strName.IndexOf(':');
	if (((-1 == index) || (index == 0)) || ((strName.GetLength() - 1) == index))
	{
		strPrefix = UniString();
		strLocalName = strName;
	}
	else
	{
		strPrefix = strName.Substring(0, index);
		strLocalName = strName.Substring(index + 1);
	}
}

//the function is provided only at Load time to speed up Load process
REFXMLNode* REFXMLNode::AppendChildForLoad(REFXMLNode* pNewChild, REFXMLDocument* pDoc)
{
	REFXMLLinkedNode* pRefNode = GetLastNode();
	REFXMLLinkedNode* pNewNode = (REFXMLLinkedNode*)pNewChild;

	if (pRefNode == NULL)
	{
		SetFirstNode(pNewNode);
		SetLastNode(pNewNode);
	}
	else
	{
		pNewNode->m_pPrev = pRefNode;
		pRefNode->m_pNext = pNewNode;
		SetLastNode(pNewNode);

		// NOTE: special-case for text nodes.
		if (pRefNode->IsText() && pNewNode->IsText())
		{
			NestTextNodes(pRefNode, pNewNode);
			goto Return;
		}
	}
	pNewNode->SetParentForLoad(this);

Return:
	pDoc->NotifyChanged();
	return pNewNode;
}

void REFXMLNode::SetParentForLoad(REFXMLNode* pNode)
{
	m_pParentNode = pNode;
}

void REFXMLNode::NestTextNodes(REFXMLNode* pPrevNode, REFXMLNode* pNextNode)
{
	BFX_ASSERT(pPrevNode->IsText());
	BFX_ASSERT(pNextNode->IsText());

	pNextNode->m_pParentNode = pPrevNode;
}

void REFXMLNode::UnnestTextNodes(REFXMLNode* pPrevNode, REFXMLNode* pNextNode)
{
	BFX_ASSERT(pPrevNode->IsText());
	BFX_ASSERT(pNextNode->IsText());

	pNextNode->m_pParentNode = pPrevNode->GetParentNode(); 
}

}	//	namespace BFX
